// =====================================================================================
// 
//       Filename:  attributs.cpp
// 
//    Description:  Implémentation des fonctions de gestion des tables attributaires
//    				d'une base de données
// 
//        Version:  1.0
//        Created:  22/07/2009 01:13:02
//       Revision:  none
//       Compiler:  g++
// 
//         Author:  François Hissel (fh), francois.hissel@m4x.org
//        Company:  
// 
// =====================================================================================

#include	<cstring>
#include	<string>
#include	<vector>
#include	<iostream>
#include	"attributs.h"

/*********************************************/
/* IMPLEMENTATION DE LA CLASSE DATAEXCEPTION */
/*********************************************/
const char* DataException::what() const throw() {
	switch (code) {
		case BAD_DESCRIPTION:return ("Description de la table incorrecte : "+message).c_str();
		case BAD_REFERENCE:return ("Référence introuvable : "+message).c_str();
		case BAD_FORMAT:return ("Mauvais format de données : "+message).c_str();
		default:return message.c_str();
	}
}

/*************************************/
/* IMPLEMENTATION DE LA CLASSE CHAMP */
/*************************************/
int Champ::taille() const {
	switch (type) {
		case ENTIER:return sizeof(int);
		case ENTIER_LONG:return sizeof(long);
		case FLOTTANT:return sizeof(float);
		case DOUBLE:return sizeof(double);
		case CHAINE:return sizeof(void*);
		default:return 0;
	}
}

/************************************************/
/* IMPLEMENTATION DE LA CLASSE DESCRIPTIONTABLE */
/************************************************/
DescriptionTable::DescriptionTable(int pnombre):_taille(0),_nombre(pnombre),_position(0),_index(0) {
	champs=new Champ[pnombre];
	positions=new int[pnombre];
}

DescriptionTable::~DescriptionTable() {
	delete[] champs;
	delete[] positions;
	delete[] _index;
}

void DescriptionTable::ajoute_champ(const Champ &c) {
	if (_position>=_nombre) throw DataException(DataException::BAD_DESCRIPTION,"Trop de champs inclus");
	positions[_position]=_taille;
	champs[_position++]=c;
	_taille+=c.taille();
	if (_position==_nombre) create_index();	// Création automatique de l'index
}

int DescriptionTable::get_index(const string &pchamp) const {
	if (_index==0) return -1;	// Si l'index n'a pas encore été construit, on sort sans réponse
//	int i=0;
//	while (i<nombre()) {
//		if (pchamp.compare(champ(i).nom)==0) return i;
//		++i;
//	}
	// Recherche dichotomique du champ à partir du tableau des index précédemment construit
	int a=0;
	int b=_nombre-1;
	while (a<b-1) {
		int c=(b+a)/2;
		int cmp=pchamp.compare(champs[_index[c]].nom);
		if (cmp==0) return _index[c];
		if (cmp>0) a=c; else b=c;
	}
	if (pchamp.compare(champs[_index[a]].nom)==0) return _index[a];
	if (b>a && pchamp.compare(champs[_index[b]].nom)==0) return _index[b];
	return -1;
}

void DescriptionTable::create_index() {
	_index=new unsigned char[_nombre];
	for (int i=0;i<_nombre;++i) _index[i]=i;
	// Tri simple du tableau dex index par un algorithme de tri à bulles, adapté pour de faibles nombres de champs (ce qui sera toujours le cas)
	for (int i=0;i<_nombre-1;++i)
		for (int j=0;j<_nombre-1-i;++j) {
			if (champs[_index[j]].nom.compare(champs[_index[j+1]].nom)>0) {
				unsigned char tmp=_index[j];
				_index[j]=_index[j+1];
				_index[j+1]=tmp;
			}
		}
}

/**********************************************/
/* IMPLEMENTATION DE LA CLASSE ENREGISTREMENT */
/**********************************************/
Enregistrement::Enregistrement(const DescriptionTable *pdescription):description(pdescription) {
	//data=(char*)malloc(pdescription->taille());
	data=new char[pdescription->taille()];
	memset(data,0,pdescription->taille());
	//for (int i=0;i<pdescription->taille();++i) data[i]=0;
}

Enregistrement::Enregistrement(const Enregistrement &e) {
	description=e.description;
	data=new char[description->taille()];
	memcpy(data,e.data,description->taille());
	for (int i=0;i<description->nombre();++i) if (description->champ(i).type==Champ::CHAINE) {
		string **source=(string**)(e.get_data(i));
		string **dest=(string**)get_data(i);
		*dest=new string(**source);
	}
}

Enregistrement::~Enregistrement() {
	//free(data);
	for (int i=0;i<description->nombre();++i) if (description->champ(i).type==Champ::CHAINE) {
		string **s=(string**)get_data(i);
		if (*s) delete *s;
	}
	delete[] data;
}

char *Enregistrement::get_data(int index) const throw (DataException) {
	if (index<0 || index>=description->nombre()) throw DataException(DataException::BAD_REFERENCE,"Index incorrect");
	return data+description->position(index);
}

char *Enregistrement::get_data(const string &pchamp) const throw (DataException) {
	int i=description->get_index(pchamp);
	if (i<0) throw DataException(DataException::BAD_REFERENCE,"Champ inexistant");
	return get_data(i);
}

void Enregistrement::set(int index,int valeur) throw (DataException) {
	if (description->champ(index).type!=Champ::ENTIER) throw DataException(DataException::BAD_FORMAT,"Entier");
	*((int*)get_data(index))=valeur;
}

void Enregistrement::set(int index,long valeur) throw (DataException) {
	if (description->champ(index).type!=Champ::ENTIER_LONG) throw DataException(DataException::BAD_FORMAT,"Entier long");
	*((long*)get_data(index))=valeur;
}

void Enregistrement::set(int index,float valeur) throw (DataException) {
	if (description->champ(index).type!=Champ::FLOTTANT) throw DataException(DataException::BAD_FORMAT,"Flottant");
	*((float*)get_data(index))=valeur;
}

void Enregistrement::set(int index,double valeur) throw (DataException) {
	if (description->champ(index).type!=Champ::DOUBLE) throw DataException(DataException::BAD_FORMAT,"Flottant double");
	*((double*)get_data(index))=valeur;
}

void Enregistrement::set(int index,const string &valeur) throw (DataException) {
	if (description->champ(index).type!=Champ::CHAINE) throw DataException(DataException::BAD_FORMAT,"Chaîne de caractères");
	string **s=(string**)get_data(index);
	if (*s) delete *s;
	*s=new string(valeur);
}

void Enregistrement::display(ostream &out,int index) throw (DataException) {
	if (index<0 || index>=description->nombre()) return;
	switch (description->champ(index).type) {
		case Champ::CHAINE:out << get_string(index);break;
		case Champ::ENTIER:out << get_int(index);break;
		case Champ::ENTIER_LONG:out << get_long(index);break;
		case Champ::FLOTTANT:out << get_float(index);break;
		case Champ::DOUBLE:out << get_double(index);break;
		default:return;
	}
}
